/*
*	Copyright(c) 2020 lutianming email：641471957@qq.com
*
*	Sheeps may be copied only under the terms of the GNU Affero General Public License v3.0
*/

#include "lhook.h"
#include "LuaHookProtocol.h"
#include <string>
#ifdef __WINDOWS__
#include "io.h"
#pragma comment(lib,"ws2_32.lib")
#else
#include <dirent.h>
#endif // __WINDOWS__

static size_t getFiles(const char* dir_path, char* buff, size_t buff_size, size_t size)
#ifdef __WINDOWS__
{
	size += snprintf(buff + size, buff_size - size, ";%s/?.lua", dir_path);
	intptr_t hFile = 0;
	struct _finddata_t fileinfo;

	char allfile[256] = { 0x0 };
	snprintf(allfile, sizeof(allfile), "%s/*", dir_path);

	char fullpath[256] = { 0x0 };
	if ((hFile = _findfirst(allfile, &fileinfo)) != -1)
	{
		do
		{
			memset(fullpath, 0, sizeof(fullpath));
			if ((fileinfo.attrib & _A_SUBDIR))
			{
				if (strcmp(fileinfo.name, ".") == 0 || strcmp(fileinfo.name, "..") == 0)
					continue;
				snprintf(fullpath, sizeof(fullpath), "%s/%s", dir_path, fileinfo.name);
				size = getFiles(fullpath, buff, buff_size, size);
			}
		} while (_findnext(hFile, &fileinfo) == 0);
		_findclose(hFile);
	}
	return size;
}
#else
{
	DIR* dir;
	struct dirent* ptr;

	if ((dir = opendir(dir_path)) == NULL)
	{
		//perror("Open dir error...");
		return size;
	}
	size += snprintf(buff + size, buff_size - size, ";%s/?.lua", dir_path);

	char fullpath[260] = { 0x0 };
	while ((ptr = readdir(dir)) != NULL)
	{
		memset(fullpath, 0, sizeof(fullpath));
		if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
			continue;
		else if (ptr->d_type == DT_DIR || ptr->d_type == DT_UNKNOWN)
		{
			snprintf(fullpath, sizeof(fullpath), "%s/%s", dir_path, ptr->d_name);
			size = getFiles(fullpath, buff, buff_size, size);
		}
	}
	closedir(dir);
	return size;
}
#endif // __WINDOWS__

int lua_State_Set_Package_Path(lua_State* L, const char* path)
{
	char buf[8192] = {0x0};
	size_t buf_size = 0;

	lua_getglobal(L, "package");
	lua_pushstring(L, "path");
	lua_gettable(L, -2);
	buf_size = snprintf(buf, sizeof(buf), "%s", lua_tostring(L, -1));
	lua_pop(L, 1);

	buf_size = getFiles(path, buf, sizeof(buf), buf_size);
	lua_pushstring(L, "path");
	lua_pushstring(L, buf);
	lua_settable(L, -3);

	lua_pop(L, 1);
	return 1;
}

#define CHECK_LOG_BUFF(size, offset) if(offset > size) return offset;
static int obj_tostring(lua_State* L, const char* split, int index, char* buf, int bufsize, int offset, std::map<const void*, bool>& table_ref) {
	switch (lua_type(L, index)) {
	case LUA_TNUMBER: {
		if (lua_isinteger(L, index))
			offset += snprintf(buf + offset, bufsize - offset, "%s%lld", split, lua_tointeger(L, index));
		else
			offset += snprintf(buf + offset, bufsize - offset, "%s%f", split, (double)lua_tonumber(L, index));
		CHECK_LOG_BUFF(bufsize, offset);
		break;
	}
	case LUA_TSTRING: {
		offset += snprintf(buf + offset, bufsize - offset, "%s", split);
		CHECK_LOG_BUFF(bufsize, offset);
		size_t len = 0;
		const char* ptr = lua_tolstring(L, index, &len);
		if (bufsize - offset > (int)len) {
			memcpy(buf + offset, ptr, len);
			offset += (int)len;
		}
		else
			return offset;
		break;
	}
	case LUA_TBOOLEAN:
		offset += snprintf(buf + offset, bufsize - offset, "%s%s", split, lua_toboolean(L, index) ? "true" : "false");
		CHECK_LOG_BUFF(bufsize, offset);
		break;
	case LUA_TNIL:
		offset += snprintf(buf + offset, bufsize - offset, "%snil", split);
		CHECK_LOG_BUFF(bufsize, offset);
		break;
	case LUA_TTABLE: {
		offset += snprintf(buf + offset, bufsize - offset, "%s{", split);
		CHECK_LOG_BUFF(bufsize, offset);
		int top = lua_gettop(L);
		lua_pushnil(L);
		lua_copy(L, index, -1);

		const void* p = lua_topointer(L, -1);
		if (table_ref.find(p) == table_ref.end()) {
			table_ref.insert(std::make_pair(p, true));
			lua_pushnil(L);
			bool first = true;
			while (lua_next(L, -2) != 0) {
				if (first)
					first = false;
				else {
					offset += snprintf(buf + offset, bufsize - offset, ",");
					CHECK_LOG_BUFF(bufsize, offset);
				}
				offset = obj_tostring(L, "", top + 2, buf, bufsize, offset, table_ref);
				CHECK_LOG_BUFF(bufsize, offset);
				offset += snprintf(buf + offset, bufsize - offset, ":");
				CHECK_LOG_BUFF(bufsize, offset);
				offset = obj_tostring(L, "", top + 3, buf, bufsize, offset, table_ref);
				CHECK_LOG_BUFF(bufsize, offset);
				lua_pop(L, 1);
			}
		}
		else {
			offset += snprintf(buf + offset, bufsize - offset, "%stable[%p]", split, p);
			CHECK_LOG_BUFF(bufsize, offset);
		}

		lua_pop(L, 1);
		offset += snprintf(buf + offset, bufsize - offset, "}");
		CHECK_LOG_BUFF(bufsize, offset);
		break;
	}
	default: {
		int tt = luaL_getmetafield(L, index, "__name");  /* try name */
		const char* kind = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : luaL_typename(L, index);
		offset += snprintf(buf + offset, bufsize - offset, "%s%s[%p]", split, kind, lua_topointer(L, index));
		CHECK_LOG_BUFF(bufsize, offset);
		if (tt != LUA_TNIL)
			lua_remove(L, -2);  /* remove '__name' */
		break;
	}
	}
	return offset;
}

static int api_ServerHookLog(lua_State* L){
	luaL_checktype(L, 1, LUA_TNUMBER);
	int log_level = (uint8_t)lua_tonumber(L, 1);
	if (shooklogid < 0 || CheckLogLevel(shooklogid, log_level))
		return 0;
	char buf[8192] = { 0x0 };
	int offset = 0;
	int argc = lua_gettop(L);
	std::map<const void*, bool> table_ref;
	for (int i = 2; i <= argc; i++) {
		offset = obj_tostring(L, i > 2 ? " " : "", i, buf, sizeof(buf), offset, table_ref);
		if ((size_t)offset >= sizeof(buf) - 3) {
			offset = MAX_LOG_LEN - 4;
			break;
		}
	}
	//offset += snprintf(buf + offset, (size_t)MAX_LOG_LEN - offset, "\r\n");
	LOG(shooklogid, log_level, "%s\n", buf);
	return 0;
}

static int api_Sleep(lua_State* L) {
	int sleep_time = (int)lua_tointeger(L, 1);
	lua_getfield(L, LUA_REGISTRYINDEX, _HookProtoName);
	LuaHookProtocol* proto = (LuaHookProtocol*)lua_topointer(L, 2);
	HTIMER timer = TimerCreate(proto, L, sleep_time, 0, [](HTIMER timer, BaseWorker* protocol, void* user_data) {
		LuaHookProtocol* proto = (LuaHookProtocol*)protocol;
		proto->time_sleep(timer);
		proto->HandleRef.erase(timer);
	});
	proto->HandleRef.insert(std::make_pair(timer, 0));
	std::map<lua_State*, int>::iterator iter = proto->TL_REF.find(L);
	if (iter == proto->TL_REF.end()) {
		lua_pushthread(L);
		int ref = luaL_ref(L, LUA_REGISTRYINDEX);
		proto->TL_REF.insert(std::make_pair(L, ref));
	}
	return lua_yield(L, 0);
}

static void accepted(HSOCKET hsock, BaseWorker* work, void* user_data, int state) {
	((LuaHookProtocol*)work)->ConnectionAccepted((LuaAccepter*)user_data, hsock, hsock->protocol);
}

static void accepted_call(BaseAccepter* accepter, HSOCKET hsock) {
	HsocketRebindWorker(hsock, ((LuaAccepter*)accepter)->proto, accepter, accepted);
}

static int get_socket_type(lua_State* L, int index) {
	lua_pushstring(L, "type");
	lua_gettable(L, index);
	int type = (int)lua_tointeger(L, -1);
	lua_pop(L, 1);
	return type;
}

static void* get_socket_handle(lua_State* L, int index) {
	lua_pushstring(L, "handle");
	lua_gettable(L, index);
	void* handle = lua_touserdata(L, -1);
	lua_pop(L, 1);
	return handle;
}

static int api_HsocketAccept(lua_State* L) {
	int type = get_socket_type(L, 1);
	luaL_argcheck(L, type == SOCKET_LISTEN, 1, "Not Support");
	//LuaAccepter* accepter = (LuaAccepter*)lua_touserdata(L, 1);
	LuaAccepter* accepter = (LuaAccepter*)get_socket_handle(L, 1);
	luaL_argcheck(L, accepter != NULL, 1, "Wrong Parameter");
	if (accepter->listen_list.empty()) {
		LuaHookProtocol* proto = accepter->proto;
		std::map<lua_State*, int>::iterator iter = proto->TL_REF.find(L);
		if (iter == proto->TL_REF.end()) {
			lua_pushthread(L);
			int ref = luaL_ref(L, LUA_REGISTRYINDEX);
			proto->TL_REF.insert(std::make_pair(L, ref));
		}
		accepter->L = L;
		return lua_yield(L, 0);
	}
	else {
		HSOCKET hsock = accepter->listen_list.front();
		accepter->listen_list.pop_front();
		//lua_pushlightuserdata(L, hsock);
		create_socket_content(L, hsock, SOCKET_CONNECT);
		return 1;
	}
}

static int api_HsocketListenStop(lua_State* L) {
	//LuaAccepter* accepter = (LuaAccepter*)lua_touserdata(L, 1);
	LuaAccepter* accepter = (LuaAccepter*)get_socket_handle(L, 1);
	std::list<HSOCKET>* free_socket = &accepter->listen_list;
	std::list<HSOCKET>::iterator iter;
	for (iter = free_socket->begin(); iter != free_socket->end(); iter++) {
		HsocketClosed(*iter);
	}
	accepter->stop();
	delete accepter;
	return 0;
}

static int api_HsocketSend(lua_State* L) {
	int type = get_socket_type(L, 1);
	luaL_argcheck(L, type == SOCKET_CONNECT, 1, "Not Support");
	//HSOCKET hsock = (HSOCKET)lua_touserdata(L, 1);
	HSOCKET hsock = (HSOCKET)get_socket_handle(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "Wrong Parameter");
	if (hsock == NULL) return 0;
	luaL_checktype(L, 2, LUA_TSTRING);
	size_t datalen = 0;
	const char* data = lua_tolstring(L, 2, &datalen);
	int argc = lua_gettop(L);
	if (argc > 2) {
		luaL_checktype(L, 3, LUA_TNUMBER);
		datalen = (int)lua_tointeger(L, 3);
	}
	lua_getfield(L, LUA_REGISTRYINDEX, _HookProtoName);
	LuaHookProtocol* proto = (LuaHookProtocol*)lua_topointer(L, -1);
	std::map<void*, int>::iterator iter_connect = proto->HandleRef.find(hsock);
	if (iter_connect != proto->HandleRef.end()) {
		HsocketSend(hsock, data, (int)datalen);
	}
	return 0;
}

static int api_HsocketRecv(lua_State* L) {
	int type = get_socket_type(L, 1);
	luaL_argcheck(L, type == SOCKET_CONNECT, 1, "Wrong Parameter");
	//HSOCKET hsock = (HSOCKET)lua_touserdata(L, 1);
	HSOCKET hsock = (HSOCKET)get_socket_handle(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "Wrong Parameter");
	if (hsock == NULL) return 0;

	lua_getfield(L, LUA_REGISTRYINDEX, _HookProtoName);
	LuaHookProtocol* proto = (LuaHookProtocol*)lua_topointer(L, -1);
	std::map<void*, int>::iterator iter_connect = proto->HandleRef.find(hsock);
	if (iter_connect == proto->HandleRef.end()) {
		lua_pushstring(L, "");
		return 1;
	}

	if (hsock->offset > 0) {
		lua_pushlstring(L, hsock->recv_buf, hsock->offset);
		hsock->offset = 0;
		return 1;
	}

	std::map<lua_State*, int>::iterator iter = proto->TL_REF.find(L);
	if (iter == proto->TL_REF.end()) {
		lua_pushthread(L);
		int ref = luaL_ref(L, LUA_REGISTRYINDEX);
		proto->TL_REF.insert(std::make_pair(L, ref));
	}
	hsock->user_ptr = L;
	return lua_yield(L, 0);
}

static int api_HsocketClose(lua_State* L) {
	//HSOCKET hsock = (HSOCKET)lua_touserdata(L, 1);
	HSOCKET hsock = (HSOCKET)get_socket_handle(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "Wrong Parameter");
	if (hsock == NULL) return 0;
	lua_getfield(L, LUA_REGISTRYINDEX, _HookProtoName);
	LuaHookProtocol* proto = (LuaHookProtocol*)lua_topointer(L, 2);
	std::map<void*, int>::iterator iter_connect = proto->HandleRef.find(hsock);
	if (iter_connect != proto->HandleRef.end()) {
		HsocketClosed(hsock);
		proto->HandleRef.erase(hsock);
	}
	return 0;
}

static int api_socket_content_close(lua_State* L) {
	int type = get_socket_type(L, 1);
	if (type == SOCKET_CONNECT) {
		return api_HsocketClose(L);
	}
	if (type == SOCKET_LISTEN) {
		return api_HsocketListenStop(L);
	}
	return 0;
}

static int api_HsocketPeerAddrGet(lua_State* L) {
	int type = get_socket_type(L, 1);
	luaL_argcheck(L, type == SOCKET_CONNECT, 1, "Wrong Parameter");
	//HSOCKET hsock = (HSOCKET)lua_touserdata(L, 1);
	HSOCKET hsock = (HSOCKET)get_socket_handle(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "hsock parm error");

	char ip[40] = { 0x0 };
	int port = 0;
	HsocketPeerAddr(hsock, ip, sizeof(ip), &port);
	lua_pushstring(L, ip);
	lua_pushinteger(L, port);
	return 2;
}

static int api_HsocketLocalAddrGet(lua_State* L) {
	//HSOCKET hsock = (HSOCKET)lua_touserdata(L, 1);
	HSOCKET hsock = (HSOCKET)get_socket_handle(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "hsock parm error");

	char ip[40] = { 0x0 };
	int port = 0;
	HsocketLocalAddr(hsock, ip, sizeof(ip), &port);
	lua_pushstring(L, ip);
	lua_pushinteger(L, port);
	return 2;
}

static int api_HsocketPeerAddrSet(lua_State* L) {
	int type = get_socket_type(L, 1);
	luaL_argcheck(L, type == SOCKET_CONNECT, 1, "Wrong Parameter");
	//HSOCKET hsock = (HSOCKET)lua_touserdata(L, 1);
	HSOCKET hsock = (HSOCKET)get_socket_handle(L, 1);
	luaL_argcheck(L, hsock != NULL, 1, "hsock parm error");
	if (hsock == NULL) return 0;
	luaL_checktype(L, 2, LUA_TSTRING);
	const char* ip = lua_tostring(L, 2);
	luaL_checktype(L, 3, LUA_TNUMBER);
	int port = (int)lua_tointeger(L, 3);

	HsocketPeerAddrSet(hsock, ip, port);
	return 0;
}

int create_socket_content(lua_State* L, void* handle, int type){
	lua_newtable(L);
	lua_pushstring(L, "handle");
	lua_pushlightuserdata(L, handle);
	lua_settable(L, -3);

	lua_pushstring(L, "type");
	lua_pushinteger(L, type);
	lua_settable(L, -3);

	if (type == SOCKET_LISTEN) {
		lua_pushstring(L, "accept");
		lua_pushcfunction(L, api_HsocketAccept);
		lua_settable(L, -3);
	}
	else {
		lua_pushstring(L, "send");
		lua_pushcfunction(L, api_HsocketSend);
		lua_settable(L, -3);

		lua_pushstring(L, "recv");
		lua_pushcfunction(L, api_HsocketRecv);
		lua_settable(L, -3);

		lua_pushstring(L, "peer_addr");
		lua_pushcfunction(L, api_HsocketPeerAddrGet);
		lua_settable(L, -3);

		lua_pushstring(L, "peer_addr_set");
		lua_pushcfunction(L, api_HsocketPeerAddrSet);
		lua_settable(L, -3);

		lua_pushstring(L, "local_addr");
		lua_pushcfunction(L, api_HsocketLocalAddrGet);
		lua_settable(L, -3);
	}

	lua_pushstring(L, "close");
	lua_pushcfunction(L, api_socket_content_close);
	lua_settable(L, -3);

	return 0;
}

static int api_HsocketListen(lua_State* L) {
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* ip = lua_tostring(L, 1);
	luaL_checktype(L, 2, LUA_TNUMBER);
	int port = (int)lua_tointeger(L, 2);
	lua_getfield(L, LUA_REGISTRYINDEX, _HookProtoName);
	LuaHookProtocol* proto = (LuaHookProtocol*)lua_topointer(L, 3);
	LuaAccepter* accepter = new LuaAccepter(accepted_call);
	if (accepter) {
		proto->HandleRef.insert(std::make_pair(accepter, 2));
		accepter->proto = proto;
		accepter->listen(ip, port);
		create_socket_content(L, accepter, SOCKET_LISTEN);
	}
	else {
		lua_pushnil(L);
	}
	return 1;
}

static int api_HsocketConnect(lua_State* L) {
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* ip = lua_tostring(L, 1);
	luaL_checktype(L, 2, LUA_TNUMBER);
	int port = (int)lua_tointeger(L, 2);
	PROTOCOL protocol = (PROTOCOL)lua_tointeger(L, 3);
	lua_getfield(L, LUA_REGISTRYINDEX, _HookProtoName);
	LuaHookProtocol* proto = (LuaHookProtocol*)lua_topointer(L, 4);
	HSOCKET hsock = HsocketConnect(proto, ip, port, protocol);
	if (hsock) {
		proto->HandleRef.insert(std::make_pair(hsock, 1));
		std::map<lua_State*, int>::iterator iter = proto->TL_REF.find(L);
		if (iter == proto->TL_REF.end()) {
			lua_pushthread(L);
			int ref = luaL_ref(L, LUA_REGISTRYINDEX);
			proto->TL_REF.insert(std::make_pair(L, ref));
		}
		hsock->user_ptr = L;
		return lua_yield(L, 0);
	}
	lua_pushnil(L);
	return 1;
}

static int api_GetHostByName(lua_State* L) {
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* name = lua_tostring(L, 1);
	char ip[40] = { 0x0 };
	GetHostByName(name, ip, sizeof(ip));
	lua_pushstring(L, ip);
	return 1;
}

static void http_response_callback(HttpTask* task, BaseWorker* worker, HttpResponse* res, int ret_state) {
	int state,nres;
	lua_State* L = (lua_State*)task->user_data;
	if (L) {
		if (ret_state == State_OK) {
			lua_newtable(L);

			lua_pushstring(L, "version");
			lua_pushstring(L, res->version.c_str());
			lua_settable(L, -3);

			lua_pushstring(L, "state_code");
			lua_pushinteger(L, res->state_code);
			lua_settable(L, -3);

			lua_pushstring(L, "state");
			lua_pushstring(L, res->state.c_str());
			lua_settable(L, -3);

			lua_pushstring(L, "header");
			lua_newtable(L);
			HttpHeader::iterator iter;
			for (iter = res->header.begin(); iter != res->header.end(); ++iter) {
				lua_pushstring(L, iter->first.c_str());
				lua_pushstring(L, iter->second.c_str());
				lua_settable(L, -3);
			}
			lua_settable(L, -3);

			lua_pushstring(L, "content");
			lua_pushstring(L, res->content.c_str());
			lua_settable(L, -3);

			lua_pushnil(L);
		}
		else {
			lua_pushnil(L);
			if (ret_state == State_Connection_Faile) lua_pushstring(L, "connection close");
			else if (ret_state == State_Connection_Faile) lua_pushstring(L, "connection faile");
		}
		state = lua_resume(L, NULL, 2, &nres);
		if (state == LUA_YIELD) {
			return;
		}
		else if (state != LUA_OK) {
			sheeps_report(L, state, (LuaHookProtocol*)worker, __func__, __LINE__);
		}
		reback_thread_state((LuaHookProtocol*)worker, L);
	}
}

static int api_HttpRequest(lua_State* L) {
	luaL_checktype(L, 1, LUA_TSTRING);
	luaL_checktype(L, 2, LUA_TSTRING);
	const char* method = lua_tostring(L, 1);
	const char* url = lua_tostring(L, 2);
	HttpHeader	header;
	const char* content = NULL;
	int type = lua_type(L, 3);
	if (type == LUA_TTABLE) {
		lua_pushnil(L);
		lua_copy(L, 3, -1);
		lua_pushnil(L);
		while (lua_next(L, -2) != 0) {
			const char* key = lua_tostring(L, -2);
			const char* val = lua_tostring(L, -1);
			header[key] = val;
			lua_pop(L, 1);
		}
		lua_pop(L, 1);
	}
	type = lua_type(L, 4);
	if (type == LUA_TTABLE) {
		// HttpHeader::iterator iter = header.find("Content-Type");
		// if (iter != header.end() && iter->second == "application/x-www-form-urlencoded") {
			
		// }
		// else {
		// 	header["Content-Type"] = "application/json";
		// }
		// cJSON* root = cJSON_CreateObject();
		// lua_pushnil(L);
		// lua_copy(L, 4, -1);
		// lua_pushnil(L);
		// while (lua_next(L, -2) != 0) {
		// 	const char* key = lua_tostring(L, -2);
		// 	const char* val = lua_tostring(L, -1);
		// 	lua_pop(L, 1);
		// }
		// lua_pop(L, 1);
	}
	else if (type == LUA_TSTRING) {
		content = lua_tostring(L, 4);
	}
	lua_getfield(L, LUA_REGISTRYINDEX, _HookProtoName);
	LuaHookProtocol* proto = (LuaHookProtocol*)lua_topointer(L, -1);
	HttpTask* task = HttpClient->request(proto, method, url, &header, content, http_response_callback);
	if (task) {
		task->user_data = L;
		return lua_yield(L, 0);
	}
	lua_pushnil(L);
	lua_pushstring(L, "system error");
	return 2;
}

int register_hook_api(lua_State* L)
{
	lua_register(L, "Log", api_ServerHookLog);
	lua_register(L, "sleep", api_Sleep);
	return 0;
}

LUAMOD_API int luaopen_pasture_socket(lua_State* L) {
	luaL_Reg l[] = {
		{"listen", api_HsocketListen},
		{"connect", api_HsocketConnect},
		{"get_host_by_name", api_GetHostByName},
		{"http_request", api_HttpRequest},
		{ NULL, NULL },
	};
	luaL_newlib(L, l);
	return 1;
}

static int api_ProxySend(lua_State* L) {
	luaL_checktype(L, 1, LUA_TTABLE);
	luaL_checktype(L, 2, LUA_TSTRING);
	lua_pushvalue(L, 1);
	lua_pushstring(L, "server");
	lua_gettable(L, -2);
	HSOCKET hsock = (HSOCKET)lua_topointer(L, -1);
	size_t len;
	const char* data = lua_tolstring(L, 2, &len);
	HsocketSend(hsock, data, (int)len);
	return 0;
}

static int api_ProxyRecv(lua_State* L){
	luaL_checktype(L, 1, LUA_TTABLE);
	luaL_checktype(L, 2, LUA_TSTRING);
	lua_pushvalue(L, 1);
	lua_pushstring(L, "client");
	lua_gettable(L, -2);
	HSOCKET hsock = (HSOCKET)lua_topointer(L, -1);
	size_t len;
	const char* data = lua_tolstring(L, 2, &len);
	HsocketSend(hsock, data, (int)len);
	return 0;
}

static int api_ProxyClose(lua_State* L){
	luaL_checktype(L, 1, LUA_TTABLE);
	return 0;
}

static int api_ProxyApiSend(lua_State* L){
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* api = lua_tostring(L, 1);
	luaL_checktype(L, 2, LUA_TNUMBER);
	int send_flow = (int)lua_tointeger(L, 2);
	luaL_checktype(L, 3, LUA_TNUMBER);
	int send_count = (int)lua_tointeger(L, 3);
	LuaHookProtocol* proto = (LuaHookProtocol*)lua_topointer(L, -1);
	proto->proxy_api_flow_send(api, send_flow, send_count);
	return 0;
}

static int api_ProxyApiRecv(lua_State* L){
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* api = lua_tostring(L, 1);
	luaL_checktype(L, 2, LUA_TNUMBER);
	int send_flow = (int)lua_tointeger(L, 2);
	luaL_checktype(L, 3, LUA_TNUMBER);
	int send_count = (int)lua_tointeger(L, 3);
	lua_getfield(L, LUA_REGISTRYINDEX, _HookProtoName);
	LuaHookProtocol* proto = (LuaHookProtocol*)lua_topointer(L, -1);
	proto->proxy_api_flow_recv(api, send_flow, send_count);
	return 0;
}

static int api_ProxyApiResponse(lua_State* L){
	luaL_checktype(L, 1, LUA_TSTRING);
	const char* api = lua_tostring(L, 1);
	luaL_checktype(L, 2, LUA_TNUMBER);
	int res_time = (int)lua_tointeger(L, 2);
	LuaHookProtocol* proto = (LuaHookProtocol*)lua_topointer(L, -1);
	proto->proxy_api_response_time(api, res_time);
	return 0;
}

LUAMOD_API int luaopen_pasture_proxy(lua_State* L) {
	luaL_Reg l[] = {
		{"send", api_ProxySend},
		{"recv", api_ProxyRecv},
		{"close", api_ProxyClose},
		{"api_send", api_ProxyApiSend},
		{"api_recv", api_ProxyApiRecv},
		{"api_response", api_ProxyApiResponse},
		{ NULL, NULL },
	};
	luaL_newlib(L, l);
	return 1;
}

static void api_Thread_Run_CallBack(BaseWorker* worker, void* data) {
	LuaHookProtocol* proto = (LuaHookProtocol*)worker;
	HTIMER timer = TimerCreate(proto, NULL, 1000, 1000, [](HTIMER timer, BaseWorker* proto, void* user_data) {
		((LuaHookProtocol*)proto)->timer_out();
		});
	proto->HandleRef.insert(std::make_pair(timer, 0));
	proto->RunScript((char*)data);
}

static int api_Thread_Run(lua_State* L) {
	size_t len;
	const char* script_file = lua_tolstring(L, 1, &len);
	lua_getfield(L, LUA_REGISTRYINDEX, _HookProtoName);
	LuaHookProtocol* proto = (LuaHookProtocol*)lua_topointer(L, -1);
	LuaHookProtocol* thread_proto = new LuaHookProtocol(proto);

	if ( !thread_proto ) {
		lua_pushnil(L);
		return 1;
	}
	char* data = (char*)malloc(len + 1);
	if (!data) {
		lua_pushnil(L);
		return 1;
	}
	memcpy(data, script_file, len);
	*(data + len) = 0x0;
	PostEvent(thread_proto, data, api_Thread_Run_CallBack);
	lua_pushlightuserdata(L, thread_proto);
	return 1;
}

LUAMOD_API int luaopen_pasture_thread(lua_State* L) {
	luaL_Reg l[] = {
		{"run", api_Thread_Run},
		{"pid", NULL},
		{"ppid", NULL},
		{"push", NULL},
		{"pull", NULL},
		{ NULL, NULL },
	};
	luaL_newlib(L, l);
	return 1;
}

LUALIB_API void luaL_open_pasture_socket(lua_State* L) {
	luaL_requiref(L, "net", luaopen_pasture_socket, 1);
	lua_pop(L, 1);  /* remove lib */
	luaL_requiref(L, "thread", luaopen_pasture_thread, 1);
	lua_pop(L, 1);  /* remove lib */
	luaL_requiref(L, "proxy", luaopen_pasture_proxy, 1);
	lua_pop(L, 1);  /* remove lib */
}